﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NSsh.Common.Types;
using NSsh.Common.Packets;
using System.Threading;
using System.Security.Principal;
using System.Diagnostics;
using NSsh.Common.Utility;
using NSsh.Server.TransportLayer;
using log4net;
using System.IO;
using NSsh.Server.ChannelLayer;
using NSsh.Server.Configuration;
using NSsh.Common.Packets.Channel;
using NSsh.Common.Packets.Channel.RequestPayloads;

namespace NSsh.Client {

    public class ClientChannel : IChannelProducer {

        private static readonly ILog log = LogManager.GetLogger(typeof(ClientChannel));

        uint ChannelId { get; set; }

        public ITransportLayerManager TransportLayerManager { get; set; }

        private Queue<Packet> _incomingData = new Queue<Packet>();

        private AutoResetEvent _incomingDataArrived = new AutoResetEvent(false);

        private Queue<Packet> _outgoingData = new Queue<Packet>();

        private AutoResetEvent _outgoingDataArrived = new AutoResetEvent(false);

        event EventHandler Closed;

        public Packet GetIncomingPacket() {
            lock (this) {
                while (_incomingData.Count == 0) {
                    _incomingDataArrived.WaitOne(-1, true);
                }

                Packet result = _incomingData.Dequeue();

                TransportLayerManager.WritePacket(new ChannelWindowAdjustPacket() {
                    RecipientChannel = ChannelId,
                    BytesToAdd = (uint)result.Length
                });

                return result;
            }
        }

        public void SendOutgoingPacket(Packet packet) {
            log.Debug("SendOutgoingPacket " + packet.PacketType);

            lock (_outgoingData) {
                _outgoingData.Enqueue(packet);
                _outgoingDataArrived.Set();
            }

            log.Debug("SendOutgoingPacket done");
        }

        public void SendData(byte[] buffer) {
            SendOutgoingPacket(new ChannelDataPacket() { RecipientChannel = ChannelId, Data = buffer });
        }

        public void SendErrorData(byte[] buffer) {
            SendOutgoingPacket(new ChannelExtendedDataPacket() {
                RecipientChannel = ChannelId,
                Data = buffer,
                ExtendedDataType = ExtendedDataType.StandardError
            });
        }

        public void Close() {
            SendOutgoingPacket(new ChannelClosePacket() {
                RecipientChannel = ChannelId
            });

            if (Closed != null) Closed(this, EventArgs.Empty);
        }

    }
}
